const fs = require("fs");
const path = require("path");
const config = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "config.json"), "utf8"));
const { execSync } = require("child_process");
const { createCanvas, loadImage } = require("canvas");
const { FFMPEG_PATH } = require("./utils/ffmpeg");
const { mergeAudioVideo, mergeAssVideo } = require("./utils/videoProcessor");
const { generateConcatVideoCommand, generateImageToVideoCommand } = require("./utils/ffmpegCommands");
const { generateASSStyles } = require("./utils/assStyles");
const { convertSRTToASSTime } = require("./utils/assStyles");

/**
 * 临时文件列表
 * 在最后一次性将tempFilse 全部删除
 */
const tempFiles = [];
// 设置临时工作空间为 config.temp对应的目录下的glenworld目录
const tempDir = path.join(config.inputGlenworldRhymes);

async function downloadBookResource(bookInfoJsonFilePath) {
  // 从bookInfoJsonFilePath中获取book的相关资源
  const bookInfo = JSON.parse(fs.readFileSync(bookInfoJsonFilePath, "utf8"));
  const pages = bookInfo.pages;
  console.log(`  Pages: ${pages.length} `, JSON.stringify(pages));
  const newPages = [];
  const page1 = pages[0];
  const textArr = page1.text;
  console.log(`  TextArr: ${textArr.length} `, JSON.stringify(textArr));
  for (const text of textArr) {
    const page = {
      image: text.image,
      text: [text],
    };
    newPages.push(page);
  }
  bookInfo.pages = newPages;
  console.log(`  Pages: ${newPages.length} `, JSON.stringify(newPages));
  fs.writeFileSync(bookInfoJsonFilePath, JSON.stringify(bookInfo, null, 2));
  const bookID = bookInfo.id;
  // 在tempFiles下创建名称为bookID的文件夹
  const bookDir = path.join(tempDir, bookID.toString());
  fs.mkdirSync(bookDir, { recursive: true });
  const bookAudio = bookInfo.audio;
  const bookCover = bookInfo.cover;
  // 封面停留时间
  const bookTitleDuration = 0.09;
  bookInfo.titleDuration = bookTitleDuration;
  const bookPages = bookInfo.pages;
  // 每个bookPage的start时间和end时间计算：
  // page.text数组中每个对象的start的最小值作为start,end的最大值作为end
  // 将计算出来的start和end赋给bookPage对象
  bookPages.forEach(bookPage => {
    const pageText = bookPage.text;
    const startTime = pageText.reduce((acc, text) => Math.min(acc, text.start), Infinity);
    const endTime = pageText.reduce((acc, text) => Math.max(acc, text.end), -Infinity);
    bookPage.start = startTime;
    bookPage.end = endTime;
  });
  // 输出bookPages中每个page的start和end时间
  bookPages.forEach(bookPage => {
    console.log(`  BookPage : start=${bookPage.start}, end=${bookPage.end}`);
  });
  const pagesImages = bookPages.map(page => page.image);
  // pagesText为每个bookPages对象的text（一个数组的合并）
  const pagesText = bookPages.map(page => page.text).flat();
  // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  // 下载资源
  // 从bookCoverAudioUrlBase下载封面音频
  // await downloadAudio(bookCoverAudioUrlBase, bookDir);
  // 下载bookAudio，保存在bookDir下
  await downloadAudio(bookAudio, bookDir);
  // 下载bookCover，报错在bookDir下
  await downloadCover(bookCover, bookDir);
  // 下载pagesImages，保存在bookDir下
  await downloadPages(pagesImages, bookDir);
  // 将pagesText转换为ass字幕文件并保存在bookDir下，样式按照config.projectSettings.textStyles
  await convertTextToAss(pagesText, bookDir, config.projectSettings.textStyles);
  return bookInfo;
}

async function downloadFile(filePath, bookDir, fileType) {
  const fileName = path.basename(filePath);
  const outputPath = path.join(bookDir, fileName);

  // Skip if file already exists
  if (fs.existsSync(outputPath)) {
    console.log(`  ${fileType} file already exists: ${outputPath}`);
    return true;
  }

  // Construct the full URL
  const url = `https://assets.glenworld.org${filePath}`;
  console.log(`  Downloading ${fileType.toLowerCase()} from ${url} to ${outputPath}`);

  // Download the file using fetch
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`  Failed to download ${fileType.toLowerCase()}: ${response.statusText}`);
  }

  const buffer = await response.arrayBuffer();
  fs.writeFileSync(outputPath, Buffer.from(buffer));
  return true;
}

async function downloadAudio(audioPath, bookDir) {
  return await downloadFile(audioPath, bookDir, "Audio");
}

async function downloadCover(coverPath, bookDir) {
  return await downloadFile(coverPath, bookDir, "Cover");
}

async function downloadPages(pagesImages, bookDir) {
  for (const imagePath of pagesImages) {
    try {
      await downloadFile(imagePath, bookDir, "Page image");
    } catch (error) {
      console.error(`Failed to download page image: ${error.message}`);
      continue;
    }
  }
  return true;
}

async function convertTextToAss(pagesText, bookDir, textStyles) {
  textStyles.MarginV = 250;
  textStyles.fontSize = 64;
  textStyles.highlightBackgroundColor = "&HFFFF00&";
  // Generate ASS file header
  let assContent = `[Script Info]
Title: Generated Subtitles
ScriptType: v4.00+
PlayResX: ${config.projectSettings.videoWidth}
PlayResY: ${config.projectSettings.videoHeight}
ScaledBorderAndShadow: yes

[V4+ Styles]
${generateASSStyles(textStyles).split("\n").slice(1).join("\n")}

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
`;

  // Process each text entry and create dialogue lines
  for (const textEntry of pagesText) {
    const startTime = convertSRTToASSTime(formatTime(textEntry.start));
    const endTime = convertSRTToASSTime(formatTime(textEntry.end));
    const text = textEntry.text.replace(/\r?\n/g, "\\N");

    assContent += `Dialogue: 0,${startTime},${endTime},Current,,0,0,0,,${text}\n`;
  }

  // Write the ASS file
  const outputPath = path.join(bookDir, "subtitles.ass");
  fs.writeFileSync(outputPath, assContent, "utf8");
  console.log(`  Subtitles saved to ${outputPath}`);
  return true;
}

function formatTime(seconds) {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);
  const millis = Math.round((seconds - Math.floor(seconds)) * 1000);

  return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")},${millis.toString().padStart(3, "0")}`;
}

// 实现downloadBooksResource函数，遍历config.inputGlenworldRhymes下的所有json文件，调用downloadBookResource函数
async function downloadBooksResource(bookID) {
  console.log(`Downloading books resource for bookID: ${bookID}`);
  const bookInfoJsonFiles = fs.readdirSync(config.inputGlenworldRhymes).filter(file => file.endsWith(".json"));
  console.log(`  BookInfoJsonFiles: ${bookInfoJsonFiles.length} `, bookInfoJsonFiles);
  for (const bookInfoJsonFile of bookInfoJsonFiles) {
    const bookInfo = await downloadBookResource(path.join(config.inputGlenworldRhymes, bookInfoJsonFile));
    console.log(  `  check book id: ${bookInfo.id},need book id: ${bookID}`);
    if (bookID === bookInfo.id) {
      console.log(`  only create book for book id: ${bookID} start`);
      await createVideoByBookResource(bookInfo);
      console.log(`  only create book for book id: ${bookID} end`);
      break;
    }
    if (!bookID) {
      await createVideoByBookResource(bookInfo);
    }
  }
}

/**
 * 通过背景图片bkImagePath上添加bookInfo.cover路径的图片，保存到coverImageOutputPath路径
 * - 注意这里添加时候，确保bookInfo.cover路径的图片宽度缩放到bkImagePath的宽度
 * @param {string} bkImagePath 背景图片路径
 * @param {JSON} bookInfo 书籍信息
 * @param {string} coverImageOutputPath 输出的封面图片路径
 */
async function createCoverImage(bkImagePath, bookInfo, coverImageOutputPath) {
  // 加载背景图和封面图

  const coverImgPath = path.join(path.dirname(bkImagePath), path.basename(bookInfo.cover));
  await createImageWithBackgroundImage(bkImagePath, coverImgPath, coverImageOutputPath);
  console.log(`  Cover image saved to ${coverImageOutputPath}`);
}

/**
 * 创建带背景图片的封面图片
 * @param {string} bkImagePath 背景图片路径
 * @param {string} coverImgPath 封面图片路径
 * @param {string} output 输出图片路径
 */
async function createImageWithBackgroundImage(bkImagePath, coverImgPath, output) {
  // 加载背景图和封面图
  const bgImg = await loadImageSync(bkImagePath);
  const coverImg = await loadImageSync(coverImgPath);

  // 创建画布，尺寸与背景图相同
  const canvas = createCanvas(bgImg.width, bgImg.height);
  const ctx = canvas.getContext("2d");

  // 绘制背景图
  ctx.drawImage(bgImg, 0, 0, bgImg.width, bgImg.height);

  // 计算封面图缩放比例，使其宽度与背景图相同
  const scaleFactor = bgImg.width / coverImg.width;
  const newWidth = bgImg.width;
  const newHeight = coverImg.height * scaleFactor;

  // 计算居中位置
  const x = 0;
  const y = (bgImg.height - newHeight) / 2;

  // 绘制缩放后的封面图
  ctx.drawImage(coverImg, x, y, newWidth, newHeight);
  // 保存最终图像
  const buffer = canvas.toBuffer("image/jpeg");
  fs.writeFileSync(output, buffer);
  console.log(`  Image with background image saved to ${output}`);
}

// 实现一个函数createVideoByBookResource,入参为书id，返回值为视频文件路径
async function createVideoByBookResource(bookInfo) {
  // 获取bookID对应的资源
  const bookID = bookInfo.id;
  const bookDir = path.join(tempDir, bookID.toString());
  // 创建视频文件路径
  const videoFilePath = path.join(bookDir, `${bookID}.mp4`);
  const listFile = path.join(bookDir, "ffmpeg_list.txt");
  tempFiles.push(listFile);
  let ffmpegListContent = "";
  const bgImage = createBgImage(bookDir);
  tempFiles.push(bgImage);
  const coverImagePath = path.join(bookDir, `${bookID}_cover.jpg`);
  tempFiles.push(coverImagePath);
  await createCoverImage(bgImage, bookInfo, coverImagePath);
  // 将bgImage转换为视频，时长为bookInfo.titleDuration,视频存储在bookDir下，名为${bookID}_cover.mp4
  const coverVideoPath = path.join(bookDir, `${bookID}_cover.mp4`);
  tempFiles.push(coverVideoPath);
  const coverVideoCommand = generateImageToVideoCommand(coverImagePath, coverVideoPath, bookInfo.titleDuration);
  console.log("  Creating cover video...");
  execSync(coverVideoCommand, { stdio: "inherit" });

  // 将file ${bookID}_cover.mp4 \n duration bookInfo.titleDuration加入ffmpegListContent
  ffmpegListContent += `file '${bookID}_cover.mp4'\n`;
  ffmpegListContent += `duration ${bookInfo.titleDuration}\n`;

  // 遍历bookInfo.pages
  let lastEnd = -1;
  for (let i = 0; i < bookInfo.pages.length; i++) {
    const page = bookInfo.pages[i];
    console.log(`  Processing page ${i}...`, JSON.stringify(page));
    // 将每一页的图像等比缩放为config.projectSettings.videoWidth的80%生成临时图像bookDir/page_[pageIndex]_image_1.jpg
    const originalImagePath = path.join(bookDir, path.basename(page.image));

    // 缩放后的图像放在bgImage的1/4处，水平居中,生成临时图像bookDir/page_[pageIndex]_image_2.jpg
    const positionedImagePath = path.join(bookDir, `page_${i}_image.jpg`);
    tempFiles.push(positionedImagePath);
    await createImageWithBackgroundImage(bgImage, originalImagePath, positionedImagePath);

    // 将bookDir/page_[pageIndex]_image_2.jpg转换为视频，时长为page.end - page.start,视频存放在bookDir下，名为page_[pageIndex].mp4
    const pageVideoPath = path.join(bookDir, `page_${i}.mp4`);
    tempFiles.push(pageVideoPath);
    let realEnd = page.start;
    if(lastEnd > -1){
      realEnd = lastEnd;
    }
    let pageDuration = page.end - realEnd;
    lastEnd = page.end;
    // pageDuration 保留两位小数
    pageDuration = pageDuration.toFixed(2);
    const pageVideoCommand = generateImageToVideoCommand(positionedImagePath, pageVideoPath, pageDuration);
    console.log(`  Creating page ${i} video...`);
    execSync(pageVideoCommand, { stdio: "inherit" });

    // 将page_[pageIndex].mp4加入到ffmpegListContent，file 文件名\n duration 秒数
    ffmpegListContent += `file 'page_${i}.mp4'\n`;
    ffmpegListContent += `duration ${pageDuration}\n`;
  }
  // 将ffmpegListContent写入listFile
  fs.writeFileSync(listFile, ffmpegListContent, "utf8");

  // 使用更适合处理统一视频格式的参数
  const outPutBookNoAudioPath = path.join(bookDir, `${bookID}_NoAudio.mp4`);
  tempFiles.push(outPutBookNoAudioPath);
  process.env.EFFECT = "fade"; //wipetl
  let videoCommand = generateConcatVideoCommand(listFile.replace(/\\/g, "/"), outPutBookNoAudioPath);
  process.env.EFFECT = undefined;
  // console.log('\n  执行视频生成命令:', videoCommand);
  console.log("\n  执行视频生成命令...");
  // 将命令写入临时脚本文件执行，避免命令行太长问题
  const listFilePath = path.dirname(listFile.replace(/\\/g, "/"));
  const scriptPath = path.join(listFilePath, "concat_script.bat");
  tempFiles.push(scriptPath);
  fs.writeFileSync(scriptPath, videoCommand, "utf-8");
  // 执行execSync，并将日志输出到./log.txt
  execSync(`"${scriptPath}"`, { stdio: "inherit" });
  // 合并bookInfo的字幕和outPutBookNoAudioPath，生成const outPutBookWithAssPath = path.join(bookDir, `${bookID}_WithAss.mp4`);
  const outPutBookWithAssPath = path.join(bookDir, `${bookID}_WithAss.mp4`);
  tempFiles.push(outPutBookWithAssPath);
  const subtitlePath = path.join(bookDir, "subtitles.ass");
  mergeAssVideo(outPutBookNoAudioPath, subtitlePath, outPutBookWithAssPath);

  // 合并bookInfo的音频和outPutBookWithAssPath，生成最终的视频，输出路径为videoFilePath
  const audioPath = path.join(bookDir, path.basename(bookInfo.audio));
  mergeAudioVideo(outPutBookWithAssPath, audioPath, videoFilePath);
}

/**
 * 基于tempDir下的book-bg.jpg创建一张图像，存储在bookDir下，名为book-bg.jpg：
 * 读取book-bg.jpg的高宽
 * 图像高度为book-bg.jpg的二倍,宽度等于book-bg.jpg的宽度
 * 图像顶部1/4处到3/4处填充为book-bg.jpg
 * 如果存在的话，就直接返回路径即可
 * @returns bookDir下的book-bg.jpg路径
 */
function createBgImage(bookDir) {
  const bgImagePath = path.join(bookDir, "book-bg.jpg");
  // 如果已经存在背景图，则直接返回路径
  if (fs.existsSync(bgImagePath)) {
    return bgImagePath;
  }

  // 查找目录中的背景图（假设为jpg格式）
  const files = fs.readdirSync(tempDir);
  const bgImageFile = files.find(file => file.includes("bg") && file.endsWith(".jpg"));
  if (!bgImageFile) {
    throw new Error("Background image not found in book directory");
  }

  const originalBgImagePath = path.join(tempDir, bgImageFile);

  // 使用ffmpeg创建新的背景图
  const createBgCommand = `"${FFMPEG_PATH}" -y -loglevel quiet -i "${originalBgImagePath}" -vf "scale=width=iw:height=iw*2,crop=w=iw:h=iw*2:x=0:y=iw/2" "${bgImagePath}"`;
  execSync(createBgCommand);

  return bgImagePath;
}

/**
 * 同步加载图像的辅助函数
 * @param {string} imagePath - 图像路径
 * @returns {object} 加载的图像对象
 */
async function loadImageSync(imagePath) {
  console.log(`  Loading image ${imagePath}...`);
  // 检查文件是否存在
  if (!fs.existsSync(imagePath)) {
    throw new Error(`Image file not found: ${imagePath}`);
  }

  // 检查文件是否为空
  const stats = fs.statSync(imagePath);
  if (stats.size === 0) {
    throw new Error(`Image file is empty: ${imagePath}`);
  }

  try {
    return await loadImage(imagePath);
  } catch (error) {
    throw new Error(`Failed to load image ${imagePath}: ${error.message}`);
  }
}
function copyBackupFilesToTempDir() {
  const backupDir = path.join(tempDir, "backup");
  if (!fs.existsSync(backupDir)) {
    throw new Error(`Backup directory not found: ${backupDir}`);
  }

  const files = fs.readdirSync(backupDir);
  for (const file of files) {
    const filePath = path.join(backupDir, file);
    console.log(`  Copying backup file ${file} to temp directory...`);
    fs.copyFileSync(filePath, path.join(tempDir, file));
  }
}
// main函数
async function main() {
  // 从tempDir的backup中拷贝全部的json到tempDir
  copyBackupFilesToTempDir();
  await downloadBooksResource("Ap1");
  tempFiles.forEach(f => {
    if (fs.existsSync(f)) fs.unlinkSync(f);
  });
}
main();
